package service

import (
	"errors"
	"math"
	"strconv"
	"strings"
	"time"

	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/httpmvc/service"
	"gitee.com/sansaniot/ssiot-core/logger"
	"gorm.io/gorm"

	"ssadmin/common/constant"
	dto "ssadmin/common/query"
	"ssadmin/common/utils"
	"ssadmin/internal/admin/models"
	"ssadmin/internal/admin/models/command"
	"ssadmin/internal/admin/models/query"
)

type DataDetail struct {
	dataDetailModel *models.SysDataDetail
	dataKeepModel   *models.SysDataKeep
	sysConfModel    *models.SysConf
	service.Service
}

func (d *DataDetail) SetDataDetailModel(m *models.SysDataDetail) *DataDetail {
	d.dataDetailModel = m
	return d
}

func (d *DataDetail) SetDataKeepModel(m *models.SysDataKeep) *DataDetail {
	d.dataKeepModel = m
	return d
}

func (d *DataDetail) SetSysConfModel(m *models.SysConf) *DataDetail {
	d.sysConfModel = m
	return d
}

func (d *DataDetail) FindDetailStat(req *query.SysDataDetailQuery) (result []map[string]interface{}, err error) {
	result = make([]map[string]interface{}, 0)
	// 默认查当天
	startTime := req.StartTime
	endTime := req.EndTime
	if len(startTime) == 0 {
		startTime = utils.ConvtTimeToStr(time.Now(), utils.TIME_FMT_YMD_000)
	}
	if len(endTime) == 0 {
		endTime = utils.ConvtTimeToStr(time.Now().AddDate(0, 0, 1), utils.TIME_FMT_YMD_000)
	}
	// 非admin的机构权限
	if len(req.DeptId) == 0 {
		if req.UserId != "1" {
			req.DeptIdIn = req.DeptIdList
		}
	} else {
		req.DeptIdIn = strings.Split(strings.ReplaceAll(req.DeptId, " ", ""), ",")
	}

	start := utils.ConvtStrToTime(startTime, utils.TIME_FMT_FULL)
	end := utils.ConvtStrToTime(endTime, utils.TIME_FMT_FULL)
	// 默认间隔1小时
	intervalH := utils.If(req.IntervalH > 0, req.IntervalH, 1).(int)
	// 不一样的查法
	if req.DataType == constant.TypeOfDevCount || req.DataType == constant.TypeOfDevActiveCount || req.DataType == constant.TypeOfUserCount {
		keyIn := utils.If(req.DataType == constant.TypeOfUserCount, []string{req.UserId}, req.DeptIdIn).([]string)
		var tmpEnd time.Time
		for start.Before(end) {
			tmpEnd = start.Add(time.Duration(intervalH) * time.Hour)
			tmpMap := map[string]interface{}{}
			resultMap := map[string]interface{}{
				"time":  utils.ConvtTimeToStr(start, utils.TIME_FMT_FULL),
				"value": 0,
			}
			if req.UserId == "1" {
				sql := "select sum(value) from ( select tt.key, tt.value, tt.create_time, row_number() over ( partition by tt.key order by tt.create_time desc) as row_num from sys_data_detail tt where tt.\"type\" = ? and tt.\"create_time\" >= ? and tt.\"create_time\" <= ?) aaa where aaa.row_num = 1"
				d.Orm.Raw(sql, req.DataType, utils.ConvtTimeToStr(start, utils.TIME_FMT_FULL), utils.ConvtTimeToStr(tmpEnd, utils.TIME_FMT_FULL)).Scan(&tmpMap)
			} else if len(keyIn) > 0 {
				sql := "select sum(value) from ( select tt.key, tt.value, tt.create_time, row_number() over ( partition by tt.key order by tt.create_time desc) as row_num from sys_data_detail tt where tt.\"type\" = ? and tt.\"create_time\" >= ? and tt.\"create_time\" <= ? and key in ?) aaa where aaa.row_num = 1"
				d.Orm.Raw(sql, req.DataType, utils.ConvtTimeToStr(start, utils.TIME_FMT_FULL), utils.ConvtTimeToStr(tmpEnd, utils.TIME_FMT_FULL), keyIn).Scan(&tmpMap)
			}
			resultMap["value"] = utils.GetCommUtil().GetIntValueFiled(tmpMap, "sum")
			result = append(result, resultMap)
			start = tmpEnd
		}
		return
	} else if req.DataType == constant.TypeOfUserActiveCount {
		// 用活按用户id查
		userModel := &models.User{}
		userModel.Orm = env.Db
		userList := make([]models.User, 0)
		_ = userModel.Model.FindList(&userList, dto.MakeCondition(query.UserQuery{CreatorIdEq: req.UserId}))
		keyIn := make([]string, 0)
		for _, u := range userList {
			keyIn = append(keyIn, u.UserId)
		}
		var tmpEnd time.Time
		for start.Before(end) {
			tmpEnd = start.Add(time.Duration(intervalH) * time.Hour)
			tmpMap := map[string]interface{}{}
			resultMap := map[string]interface{}{
				"time":  utils.ConvtTimeToStr(start, utils.TIME_FMT_FULL),
				"value": 0,
			}
			if req.UserId == "1" {
				sql := "select count(DISTINCT(key)) from sys_data_detail where type = ? and create_time >= ? and \"create_time\" <= ? "
				d.Orm.Raw(sql, req.DataType, utils.ConvtTimeToStr(start, utils.TIME_FMT_FULL), utils.ConvtTimeToStr(tmpEnd, utils.TIME_FMT_FULL)).Scan(&tmpMap)
			} else if len(keyIn) > 0 {
				sql := "select count(DISTINCT(key)) from sys_data_detail where type = ? and create_time >= ? and \"create_time\" <= ? and key in ?"
				d.Orm.Raw(sql, req.DataType, utils.ConvtTimeToStr(start, utils.TIME_FMT_FULL), utils.ConvtTimeToStr(tmpEnd, utils.TIME_FMT_FULL), keyIn).Scan(&tmpMap)
			}
			resultMap["value"] = utils.GetCommUtil().GetIntValueFiled(tmpMap, "count")
			result = append(result, resultMap)
			start = tmpEnd
		}
		return
	} else if req.DataType == constant.TypeOfDevEventList {
		keyIn := make([]string, 0)
		snLocation := make(map[string][]byte)
		// 分组可见(设备sn)
		devModel := &models.SysDev{}
		devModel.Orm = d.Orm
		devList := make([]models.SysDev, 0)
		q := query.SysDevQuery{}
		if len(req.DeptIdIn) > 0 {
			q.DeptIdIn = req.DeptIdIn
		}
		_ = devModel.FindList(&devList, dto.MakeCondition(q))
		for _, sysDev := range devList {
			keyIn = append(keyIn, sysDev.Sn)
			snLocation[sysDev.Sn] = sysDev.Location
		}
		if len(keyIn) == 0 {
			return
		}
		devCache, _ := env.Cache.HashGetAll(constant.DeviceEasyCache)
		eventCache, _ := env.Cache.LRange(constant.DevEventCache, 0, -1)
		for _, ec := range eventCache {
			m := utils.GetCommUtil().BytesToMap([]byte(ec))
			// s过滤
			tmpSn, ok := m["sn"]
			if !ok || tmpSn == nil || !utils.GetCommUtil().StrIn(tmpSn.(string), keyIn) {
				continue
			}
			// 时间范围
			tmpTime, ok := m["time"]
			if !ok || tmpTime == nil {
				continue
			}
			if len(req.StartTime) > 0 && tmpTime.(string) < req.StartTime {
				continue
			}
			if len(req.EndTime) > 0 && tmpTime.(string) > req.EndTime {
				continue
			}
			// 设备名称
			if utils.GetCommUtil().GetIntValueFiled(m, "type") == 1 {
				if dc, ok := devCache[tmpSn.(string)]; ok {
					m["name"] = utils.GetCommUtil().BytesToMap([]byte(dc))["name"]
				}
			}
			// 位置信息
			if loc, ok := snLocation[tmpSn.(string)]; ok {
				m["location"] = nil
				locMap := utils.GetCommUtil().BytesToMap(loc)
				if locMap["lng"] != nil && locMap["lat"] != nil {
					m["location"] = locMap
				}
			}
			result = append(result, m)
			// 限查
			if req.LimitCount > 0 && len(result) >= req.LimitCount {
				return
			}
		}
		return
	}

	// 根据业务按类型过滤
	keyIn := make([]string, 0)
	if req.UserId != "1" {
		switch req.DataType {
		case constant.TypeOfDevDataNum:
			fallthrough
		case constant.TypeOfAlarmNum:
			fallthrough
		case constant.TypeOfDevDataSize:
			// 分组可见(设备sn)
			devModel := &models.SysDev{}
			devModel.Orm = d.Orm
			devList := make([]models.SysDev, 0)
			q := query.SysDevQuery{}
			if len(req.DeptIdIn) > 0 {
				q.DeptIdIn = req.DeptIdIn
			}
			_ = devModel.FindList(&devList, dto.MakeCondition(q))
			for _, sysDev := range devList {
				keyIn = append(keyIn, sysDev.Sn)
			}
		case constant.TypeOfRuleProcs:
			if req.AgencyId == req.UserId {
				// 租户可见(规则id)
				ruleModel := &models.Rule{}
				ruleModel.Orm = d.Orm
				ruleList := make([]models.Rule, 0)
				_ = ruleModel.FindList(&ruleList, dto.MakeCondition(query.RuleGetReq{Creator: req.UserId}))
				for _, rule := range ruleList {
					keyIn = append(keyIn, rule.Id)
				}
			}
		case constant.TypeOfForwardNum:
			if req.AgencyId == req.UserId {
				// 租户可见(转发服务id)
				forwardModel := &models.SysForwardServer{}
				forwardModel.Orm = d.Orm
				fsList := make([]models.SysForwardServer, 0)
				_ = forwardModel.FindList(&fsList, dto.MakeCondition(query.ForwardServerQuery{AgencyIdEq: req.UserId}))
				for _, fos := range fsList {
					keyIn = append(keyIn, fos.Id)
				}
			}
		case constant.TypeOfNotifyNum:
			// 按用户id可见
			keyIn = append(keyIn, req.UserId)
		default:
			err = errors.New("请指定查询数据类型")
			return
		}
		if len(keyIn) == 0 {
			// 空数据
			var tmpEnd time.Time
			for start.Before(end) {
				tmpEnd = start.Add(time.Duration(intervalH) * time.Hour)
				result = append(result, map[string]interface{}{
					"time":  utils.ConvtTimeToStr(start, utils.TIME_FMT_FULL),
					"value": 0,
				})
				start = tmpEnd
			}
			d.Log.Warn("查无数据")
			return
		}
	}

	// 起止时间内，各小时统计
	var tmpEnd time.Time
	for start.Before(end) {
		tmpEnd = start.Add(time.Duration(intervalH) * time.Hour)
		tmpMap := map[string]interface{}{}
		q := query.SysDataDetailQuery{TypeEq: req.DataType, TimeGte: utils.ConvtTimeToStr(start, utils.TIME_FMT_FULL), TimeLte: utils.ConvtTimeToStr(tmpEnd, utils.TIME_FMT_FULL)}
		if len(keyIn) > 0 {
			q.KeyIn = keyIn
		}
		// 求sum(value)
		d.Orm.Table(d.dataDetailModel.TableName()).Select("sum(value)").Scopes(dto.MakeCondition(q)).Scan(&tmpMap)
		val := utils.GetCommUtil().GetIntValueFiled(tmpMap, "sum")
		result = append(result, map[string]interface{}{
			"time":  utils.ConvtTimeToStr(start, utils.TIME_FMT_FULL),
			"value": val,
		})
		start = tmpEnd
	}
	return
}

func (d *DataDetail) SaveDataKeep(req *command.DataKeepReq) (err error) {
	err = d.dataKeepModel.UpsertDoUpdate(&models.SysDataKeep{
		AgencyId:   req.AgencyId,
		Keep:       req.Keep,
		CreateTime: dto.LocalTime(time.Now()),
	}, []string{"agency_id"}, []string{"keep", "create_time"})
	return
}

func (d *DataDetail) SaveFileClean(req *command.FileCleanReq) (err error) {
	return d.sysConfModel.UpsertDoUpdate(&req.Config, []string{"key"}, []string{"value"})
}

func (d *DataDetail) SysDataSync(req *command.TableSync) (result interface{}, err error) {
	// 30s内有效
	if math.Abs(float64(req.Ts-time.Now().UnixMilli())) > 30*1000 {
		err = errors.New("ts请求非法")
		return
	}
	reqTs := strconv.FormatInt(req.Ts, 10)
	var decryptTs string
	if decryptTs, err = utils.AesDecrypt(req.Sk, constant.SALTKEY); err != nil || decryptTs != reqTs {
		err = errors.New("sk请求非法")
		return
	}
	if req.Type == "query" {
		result = constant.DataSyncUrlCache
		return
	}
	tableNames := make([]string, 0)
	tableName := strings.ReplaceAll(req.TableName, " ", "")
	if len(tableName) > 0 {
		tableNames = strings.Split(tableName, ",")
	}
	flag := utils.If(req.Switch == "open", "open", "close").(string)
	if req.Type == "slave" {
		// 响应(本地)主节点的数据
		result, err = sendMasterData(d.Orm, tableNames, flag)
		// lora网关分发配置更新
		//_, _ = env.Cache.LPush(constant.LoraSlaveMqttConf, req.Mq)
	} else {
		// 请求同步指定主节点数据
		err = syncMasterData(d.Orm, tableNames, req.Url, flag)
	}
	return
}

func syncMasterData(db *gorm.DB, tableNames []string, ssUrl, flag string) (err error) {
	if len(ssUrl) == 0 {
		err = errors.New("主三三地址未指定")
		return
	}
	// 执行
	var resp map[string]interface{}
	if resp, err = ReqHttp(ssUrl, flag, tableNames); err != nil {
		return
	}
	if resp["code"] == nil || resp["code"].(float64) != 200 {
		errMsg := utils.If(resp["message"] != nil, resp["message"], "主三三数据同步接口异常").(string)
		err = errors.New(errMsg)
		return
	}
	if resp["result"] == nil {
		err = errors.New("查无数据")
		return
	}

	// url缓存处理
	lastUrl, _ := env.Cache.Get(constant.DataSyncUrl)
	if len(lastUrl) > 0 && lastUrl != ssUrl {
		// 与上次不同url，自动close上次的mq分发
		go func() {
			if _, err = ReqHttp(lastUrl, "close", []string{}); err != nil {
				logger.Error(err)
			}
		}()
	}
	if flag == "open" {
		_ = env.Cache.Set(constant.DataSyncUrl, ssUrl, -1)
		constant.DataSyncUrlCache = ssUrl
	} else {
		_ = env.Cache.Del(constant.DataSyncUrl)
		constant.DataSyncUrlCache = ""
	}

	// 解密解析数据内容
	result := resp["result"].(string)
	if len(result) == 0 {
		return
	}
	var decryptResult string
	if decryptResult, err = utils.AesDecrypt(result, constant.SALTKEY); err != nil {
		return
	}
	resultMap := utils.GetCommUtil().BytesToMap([]byte(decryptResult))
	for tableName, r := range resultMap {
		rows := utils.GetCommUtil().DataToMapArray(r)
		if err = db.Exec("DELETE FROM " + tableName).Error; err != nil {
			return
		}
		if len(rows) > 0 {
			if err = db.Table(tableName).Create(rows).Error; err != nil {
				return
			}
		}
		if tableName == (models.SysDev{}).TableName() {
			// 设备相关缓存
			_ = env.Cache.Del(constant.DeviceEasyCache)
			_ = env.Cache.Del(constant.DeviceDeptCache)
			if len(rows) > 0 {
				cacheMap := map[string]interface{}{}
				cacheMap2 := map[string]interface{}{}
				ut := utils.GetCommUtil()
				for _, row := range rows {
					sn := ut.GetStringValueFiled(row, "sn")
					m := map[string]interface{}{
						"type":         ut.GetStringValueFiled(row, "type"),
						"name":         ut.GetStringValueFiled(row, "name"),
						"productModel": ut.GetStringValueFiled(row, "product_model"),
					}
					cacheMap[sn] = string(ut.MapToBytes(m))
					cacheMap2[sn] = ut.GetStringValueFiled(row, "dept_id")
				}
				err = env.Cache.HashSet(constant.DeviceEasyCache, cacheMap)
				err = env.Cache.HashSet(constant.DeviceDeptCache, cacheMap2)
			}
		}
		logger.Infof("数据同步[%v]完成", tableName)
	}
	return
}

func ReqHttp(ssUrl, flag string, tableNames []string) (resp map[string]interface{}, err error) {
	ts := time.Now().UnixMilli()
	var encryptTs string
	encryptTs, err = utils.AesEncrypt(strconv.FormatInt(ts, 10), constant.SALTKEY)
	if err != nil {
		return
	}
	mqconf := map[string]interface{}{
		flag: map[string]interface{}{
			"host":   env.DefaultConfig.Mqtt.Host,
			"port":   strconv.Itoa(int(env.DefaultConfig.Mqtt.Port)),
			"user":   env.DefaultConfig.Mqtt.User,
			"passwd": env.DefaultConfig.Mqtt.Passwd,
		},
	}
	param := map[string]interface{}{
		"tableName": strings.Join(tableNames, ","),
		"sk":        encryptTs,
		"ts":        ts,
		"type":      "slave",
		"mq":        string(utils.GetCommUtil().MapToBytes(mqconf)),
		"switch":    flag,
	}
	header := make(map[string]string)
	header["Authentication"] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjIwMzkwMTc1MjYsIm9yaWdfaWF0IjoxNjc5MDE3NTI2LCJ1c2VybmFtZSI6ImFkbWluIn0.-BGpKJPVLGS-iAMz6VCeKqYhtKaor_zaa-tbOgImTII"
	var resultJson interface{}
	err = utils.HttpPostJson(ssUrl+"/api/sys/data/sync", param, header, &resultJson)
	if err != nil {
		logger.Error(err)
		err = errors.New("主三三数据同步接口异常")
		return
	}
	if resultJson == nil {
		err = errors.New("主三三数据同步接口异常")
		return
	}
	resp = resultJson.(map[string]interface{})
	return
}

func sendMasterData(db *gorm.DB, tableNames []string, flag string) (encryptResult string, err error) {
	if flag == "open" {
		result := make(map[string][]map[string]interface{})
		for _, tableName := range tableNames {
			rows := make([]map[string]interface{}, 0)
			if err = db.Raw("select * from " + tableName).Scan(&rows).Error; err != nil {
				return
			}
			result[tableName] = rows
		}
		// 加密传输
		encryptResult, err = utils.AesEncrypt(utils.GetCommUtil().InterfaceToStr(result), constant.SALTKEY)
	}
	return
}
